package org.apache.commons.logging.impl.weakhashtableutils;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

/**
 * Wrapper giving correct symantics for equals and hashcode
 */
final class Referenced {
    
    private final WeakReference reference;
    private final int           hashCode;

    /**
     * 
     * @throws NullPointerException if referant is <code>null</code>
     */        
    Referenced(Object referant) {
        reference = new WeakReference(referant);
        // Calc a permanent hashCode so calls to Hashtable.remove()
        // work if the WeakReference has been cleared
        hashCode  = referant.hashCode();
    }
    
    /**
     * 
     * @throws NullPointerException if key is <code>null</code>
     */
    Referenced(Object key, ReferenceQueue queue) {
        reference = new WeakKey(key, queue, this);
        // Calc a permanent hashCode so calls to Hashtable.remove()
        // work if the WeakReference has been cleared
        hashCode  = key.hashCode();

    }
    
    public int hashCode() {
        return hashCode;
    }
    
    Object getValue() {
        return reference.get();
    }
    
    public boolean equals(Object o) {
        boolean result = false;
        if (o instanceof Referenced) {
            Referenced otherKey = (Referenced) o;
            Object thisKeyValue = getValue();
            Object otherKeyValue = otherKey.getValue();
            if (thisKeyValue == null) {                     
                result = (otherKeyValue == null);
                
                // Since our hashcode was calculated from the original
                // non-null referant, the above check breaks the 
                // hashcode/equals contract, as two cleared Referenced
                // objects could test equal but have different hashcodes.
                // We can reduce (not eliminate) the chance of this
                // happening by comparing hashcodes.
                if (result == true) {
                    result = (this.hashCode() == otherKey.hashCode());
                }
                // In any case, as our c'tor does not allow null referants
                // and Hashtable does not do equality checks between 
                // existing keys, normal hashtable operations should never 
                // result in an equals comparison between null referants
            }
            else
            {
                result = thisKeyValue.equals(otherKeyValue);
            }
        }
        return result;
    }
}